using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Framework.Serialization;
using IQIGame.Onigao.Logic;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using IQIGame.Onigao.Game;
using UnityEditor;

namespace IQIGame.Onigao.GamePlay.Editors
{
    public class SkillEditorBlackboard : Singleton<SkillEditorBlackboard>
    {
        public const string PrefabBytesPath = "Assets/Res/12_ModuleCfg/Skill/Prefab.bytes";
        public const string PrefabJsonPath = "Assets/EditorConfig/SkillConfig/Preview/Prefab.json";

        private SkillEditorBlackboard()
        {
        }

        private List<SkillPartConfig> parts = new List<SkillPartConfig>();
        private List<TriggerConfig> triggers = new List<TriggerConfig>();

        private Dictionary<long, SkillConditionConfig> conds = new Dictionary<long, SkillConditionConfig>();
        private List<SkillEffective_PrefabSource_ConfigData> packagePrefab = new List<SkillEffective_PrefabSource_ConfigData>();

        private Dictionary<string, string> prefabPathCache = new Dictionary<string, string>();

        public static List<long> guidCheck = new List<long>();

        public void AddPart(SkillPartConfig part)
        {
            this.parts.Add(part);
        }

        public void AddTrigger(TriggerConfig rTrigger)
        {
            this.triggers.Add(rTrigger);
        }

        public void AddPrefab(SkillEffective_PrefabSource_ConfigData rConfig)
        {
            if (string.IsNullOrEmpty(rConfig.key))
            {
                return;
            }
            this.packagePrefab.Add(rConfig);
        }

        public void Clear()
        {
            this.parts.Clear();
            this.triggers.Clear();
            this.packagePrefab.Clear();
            this.prefabPathCache.Clear();
        }

        public void Package(string graphPath, bool bPreview = true, bool packageAll = false)
        {
            var rAssetName = Path.GetFileNameWithoutExtension(graphPath);
            var rPath = Path.ChangeExtension(graphPath, ".bytes").Replace("EditorConfig/SkillConfig", "Res/12_ModuleCfg/Skill");

            // 校验一下Part的类型与数量
            var rMains = this.parts.FindAll((x) => x.partType == (int)ESkillPartType.Main);
            if (rMains.Count > 1)
            {
                this.Clear();
                throw new Exception($"{graphPath}导出出错： 执行Part每个技能最多只能有一个");
            }
            var rEnables = this.parts.FindAll((x) => x.partType == (int)ESkillPartType.Enable);
            if (rEnables.Count > 1)
            {
                this.Clear();
                throw new Exception($"{graphPath}导出出错： 启动Part每个技能最多只能有一个");
            }
            var rDisables = this.parts.FindAll((x) => x.partType == (int)ESkillPartType.Disable);
            if (rDisables.Count > 1)
            {
                this.Clear();
                throw new Exception($"{graphPath}导出出错： 结束Part每个技能最多只能有一个");
            }

            var rSkill = new SkillConfig()
            {
                name = rAssetName
            };
            // 把设置的lastID转为NextID
            for (int i = 0; i < this.parts.Count; i++)
            {
                var rPart = this.parts[i];
                rPart.effectives.Sort((x, y) => x.index.CompareTo(y.index)); // 排序
            }
            rSkill.parts = new List<SkillPartConfig>();
            rSkill.parts.AddRange(this.parts);

            rSkill.triggers = new List<TriggerConfig>();
            rSkill.triggers.AddRange(this.triggers);

            var rBuf = new ByteBuf();
            rSkill.Serialize(rBuf);

            var rDirPath = Path.GetDirectoryName(rPath);
            if (!Directory.Exists(rDirPath))
            {
                Directory.CreateDirectory(rDirPath);
            }
            File.WriteAllBytes(rPath, rBuf.Bytes);

            if (this.packagePrefab.Count > 0)
            {
                this.ExportPrefab(graphPath, false);
            }

            // 再反序列化出来做预览
            if (bPreview)
            {
                var rPreviewBuf = new ByteBuf(rBuf.Bytes);
                var rSkillPreview = new SkillConfig();
                rSkillPreview.DeSerialize(rPreviewBuf);
                var rJson = JsonConvert.SerializeObject(rSkillPreview, Formatting.Indented);
                var rPreViewPath = Path.ChangeExtension(graphPath, ".json").Replace("EditorConfig/SkillConfig", "EditorConfig/SkillConfig/Preview");
                rDirPath = Path.GetDirectoryName(rPreViewPath);
                if (!Directory.Exists(rDirPath))
                {
                    Directory.CreateDirectory(rDirPath);
                }

                // 只在全体导出时检测
                // 所有part，target action，condition有GUID 检查所有GUID是否有重复
                try
                {
                    if (packageAll)
                    {
                        for (int i = 0; i < rSkillPreview.parts.Count; i++)
                        {
                            var rPart = rSkillPreview.parts[i];
                            for (int j = 0; j < rPart.effectives.Count; j++)
                            {
                                var rEffect = rPart.effectives[j];
                                if (rEffect.customConfig is SkillEffective_Package_ConfigData rPackage)
                                {
                                    for (int k = 0; k < rPackage.actions.Count; k++)
                                    {
                                        var rAction = rPackage.actions[k];
                                        if (guidCheck.Contains(rAction.guid))
                                        {
                                            throw new Exception();
                                        }
                                        guidCheck.Add(rAction.guid);
                                    }
                                }
                                var rTarget = rEffect.target;
                                if (guidCheck.Contains(rTarget.guid))
                                {
                                    throw new Exception();
                                }
                                guidCheck.Add(rTarget.guid);
                            }
                            if (guidCheck.Contains(rPart.guid))
                            {
                                throw new Exception();
                            }
                            guidCheck.Add(rPart.guid);

                            if (rPart.condition != null)
                            {
                                if (guidCheck.Contains(rPart.condition.guid))
                                {
                                    throw new Exception();
                                }
                                guidCheck.Add(rPart.condition.guid);
                            }
                        }
                        for (int i = 0; i < rSkillPreview.triggers.Count; i++)
                        {
                            var rCondition = rSkillPreview.triggers[i].condition;
                            if (rCondition != null)
                            {
                                if (guidCheck.Contains(rCondition.guid))
                                {
                                    throw new Exception();
                                }
                                guidCheck.Add(rCondition.guid);
                            }
                        }
                    }
                }
                catch
                {
                    LogGame.LogError("技能配置出现重复ID");
                }

                File.WriteAllText(rPreViewPath, rJson);

                if (File.Exists(PrefabBytesPath))
                {
                    var rByteBuf = new ByteBuf(File.ReadAllBytes(PrefabBytesPath));
                    var rPrefabList = rByteBuf.DeSerializeList<SkillEffective_PrefabSource_ConfigData>();
                    if (rPrefabList.Count > 0)
                    {
                        var rPrefabJson = JsonConvert.SerializeObject(rPrefabList, Formatting.Indented);
                        File.WriteAllText(PrefabJsonPath, rPrefabJson);
                    }
                }
            }

            this.Clear();
            LogGame.Log($"{rPath} 写入完成");
            if (!packageAll)
            {
                AssetDatabase.Refresh();
            }
        }

        public void ExportPrefab(string graphPath, bool refresh)
        {
            var rPrefabList = new List<SkillEffective_PrefabSource_ConfigData>();
            if (File.Exists(PrefabBytesPath))
            {
                try
                {
                    var rByteBuf = new ByteBuf(File.ReadAllBytes(PrefabBytesPath));
                    rPrefabList = rByteBuf.DeSerializeList<SkillEffective_PrefabSource_ConfigData>();
                }
                catch
                {
                    LogGame.LogError("读取预制体失败，需要手动清理预设配置后重新导出");
                    throw;
                }
            }

            foreach (var rPrefab in this.packagePrefab)
            {
                rPrefab.prefabPath = graphPath;
                var nOldIndex = rPrefabList.FindIndex((x) => x.key == rPrefab.key);
                if (nOldIndex == -1)
                {
                    rPrefabList.Add(rPrefab);
                }
                else
                {
                    rPrefabList[nOldIndex] = rPrefab;
                }
            }
            var _buf = new ByteBuf();
            SerializeByteBufUtil.SerializeList(_buf, rPrefabList);
            File.WriteAllBytes(PrefabBytesPath, _buf.Bytes);
            if (refresh)
            {
                AssetDatabase.Refresh();
            }
        }

        public void ClearPrefab()
        {
            File.Delete(PrefabBytesPath);
            AssetDatabase.Refresh();
        }

        public List<string> GetPrefabList()
        {
            if (this.prefabPathCache.Count <= 0)
            {
                this.InitPrefabPathCache();
            }
            return this.prefabPathCache.Select(x => x.Key).ToList();
        }

        public string GetPrefabGraphPath(string prefabKey)
        {
            if (this.prefabPathCache.Count <= 0)
            {
                this.InitPrefabPathCache();
            }
            if (this.prefabPathCache.TryGetValue(prefabKey, out var rPath))
            {
                return rPath;
            }
            return string.Empty;
        }

        private void InitPrefabPathCache()
        {
            this.prefabPathCache.Clear();
            var rPath = "Assets/" + PathConstant.GetSkillPrefabPath();
            if (File.Exists(rPath))
            {
                var rBuf = new ByteBuf(File.ReadAllBytes(rPath));
                var rList = rBuf.DeSerializeList<SkillEffective_PrefabSource_ConfigData>();
                foreach (var rConfig in rList)
                {
                    this.prefabPathCache.Add(rConfig.key, rConfig.prefabPath);
                }
            }
        }
    }
}